10章 変数の使用
10.4.3 moch5oMaki.icon
前半部分は変数の使い方、スコープの話。
C言語を前提としている書き方が続く箇所もあってピンとこないところもあったのが正直な感想。
ちょっとした雑談
複数言語をやるということ
モチベーションは言語そのものに興味あるのでけっこうある
競技プログラミングでやって実装に困らないレベルまでやったりする
案件必要になるたびにやる
10.1 データリテラシー
データリテラシーテスト
moch5oMaki.icon半分くらい分からないな、と思ったら関係ない用語も混ざっていたらしい。ちょっといじわるw
列挙型と配列の違いが分からなかったので調べた
配列と似たデータ構造に「列挙型」があります。配列は変数を格納するため、各要素の値を自在に変更できますが、列挙型は複数の定数を並べてそれに名前を付けたものです。
moch5oMaki.iconなるほどーと思っていたら、PHPでenumを実装している人が結構いた。みんなenum好きなんだw
https://gyazo.com/19da395b22aafe7915aa4c9b9955a567
列挙型にふるまいを持たせられるので便利(メソッドが呼べる)
単純な数値だと特に静的型付け言語とかでフラグを渡すとき(int型)、フラグ同士の演算とかインクリメントとかできてしまう
型安全!
10.2 変数宣言のガイドライン
暗黙の宣言
データを明示的に宣言しなければならない言語では、基本的にデータの扱いに慎重になることが求められる。それが、そうした言語の利点の1つである。
moch5oMaki.iconたしかにそうだし、データの扱いに慎重になるというのは型の厳密さを求めていく今のトレンドにも通ずる
暗黙の宣言に対応する言語でプログラミングする際のアドバイス
コンパイラによって暗黙の宣言を無効にする
すべての変数を宣言する
1つの変数に2つの名前をつけてしまわないように命名規則を使用する
コンパイラなどの生成する相互参照リストを使って変数名を確認する
10.3 変数の初期化のガイドライン
不適切なデータの初期化は、コンピュータプログラミングのエラーの温床である。初期化の問題を防ぐための効果的な方法を編み出せば、デバッグにかかる時間を大幅に短縮できる。
不適切な初期化による問題は、変数に予想外の初期値が含まれていることに起因する。
moch5oMaki.iconうんうん、まちがいない
この節、途中からC言語想定してるっぽいことが多くてよく分からないところがあったので、気になる点だけ列挙してます。他に気になる項目があったらぜひ教えて欲しいです。
ガイドラインより抜粋
変数は宣言時に初期化する
変数は最初に使用する場所の近くで初期化する
言語によっては宣言時に初期化できないので注意
moch5oMaki.icon最初は全体が短いコードだしいいかなって思ってやってしまいそう(だいたいあとから行数が膨らむ)
変数の宣言と定義は、最初に使用する場所の近くで行うのが理想的である
できるだけfinalまたはconstを使用する
カウンタとアキュムレータは特に注意する(変数 i, j, k, sum, total など)
アキュムレータ
アキュムレータ(英: Accumulator)は、コンピュータにおいて、演算装置による演算結果を累積する、すなわち総和を得るといったような計算に使うレジスタや変数のことである。 Wikipediaより
クラスのメンバデータはコンストラクタで初期化する
コンストラクタで割り当てたメモリは、デストラクタで解放する
10.4 スコープ
変数の「スコープ’」とは、変数の知名度、つまりどれくらい有名かについて考えることを示す。スコープは可視性とも呼ばれ、変数がプログラムのどの範囲まで知れ渡っていて、どの範囲で参照できるのかを意味する。
この節ではスコープを適切に管理するためのガイドラインが示されている。
「どれくらい有名か」って面白いしわかりやすいたとえakht.icon
10.4.1 変数の参照はまとめて
変数の「持続間隔」
変数が定義されてから、参照されるまでの行数(参照されるごとに持続間隔が割り出せる)
平均持続間隔は、個々の持続間隔の平均から割り出される
moch5oMaki.icon計算することはほとんどなさそうだけど意識しておく分には良さそう
10.4.2 変数の「寿命」はできるだけ短く
変数の寿命とは、変数が存続する期間内に存在する行数
最初に参照されたステートメントから最後に参照されたステートメントまでを変数の寿命と考える
変数が参照される回数は考慮しない
変数の「寿命が長い」ことは、変数が多くのステートメントにまたがって存続することを意味する。変数の「持続間隔」は、変数の参照どうしがどれくらい接近しているかを示す。
持続間隔と寿命はどちらも短いほうがよく、脆弱性の窓を小さくすることができる
変数の寿命が短いと、初期化のエラーも発生しにくいしコードも読みやすくなる
moch5oMaki.icon 目安がないから難しいけど、なんとなく寿命がこれが長すぎるようだったらリファクタリングで関数の切り出しとかをやったほうがいいという合図かもしれない
実際に変数の参照を近くにまとめておくとリファクタリングしやすくなる、と書かれている
10.4.3 変数の寿命の測定
実際のコードを例にして変数の寿命の測定方法についての解説(コードは割愛)
moch5oMaki.icon行数をカウントするだけとはいえ、実際のプロダクトでカウントしたりチェックしたりするのはちょっと無理そう
持続間隔と寿命という概念をグローバル変数にあてはめてみると、グローバル変数は持続間隔が広大で、寿命がとても長いことが分かる。このことも、グローバル変数の使用を避ける理由の1つになる。
moch5oMaki.iconこれは間違いない
10.4.4 akht.icon
10.4.4 スコープを最小限に抑えるためのガイドライン
ループで使う変数はループの直前で初期化する
変数の値を使う直前まで、変数に値を代入しない
関連するステートメントをまとめる
oldとnewをそれぞれまとめる
関連するステートメントをまとめて別のルーチンに分ける
最初は狭いスコープから始めて必要に応じてスコープを拡げる
10.4.5 スコープを最小限に抑えることについて
「便利さ」と「理解しやすさ」をどう捉えるか
グローバルスコープは便利である
変数にアクセスしやすい
引数リストやクラススコープの規則に振り回されることがなくなる
ローカルスコープは理解しやすい
情報を隠蔽する
覚えておかなければならないことを減らせる
「便利さ」と「理解しやすさ」はプログラムを書くことと読むことのどちらに重きをおくかの違い
スコープを広くすればプログラムは書きやすいが読みづらく理解しづらく変更しづらい
スコープが狭ければそれらの問題は解消される
10.5 永続性
「永続性」とは、1つのデータの寿命を示すもう1つの指標である。
変数の永続性(いくつかの例)
特定のコードブロックまたはルーチンがスコープを外れるまで
C++, Javaのforループ内で宣言された変数など
プログラマは指定した期間まで
Javaではnewされた変数はGCによって回収されるまで存続する
プログラムが終了するまで
ほとんどの言語のグローバル変数、C++, Javaのstatic変数
永久
DBやファイルに保存されるデータを含む
永続性の最大の問題は、変数の永続性を実際よりも長いと勘違いすることによるものだ。
変数が酸っぱくなってしまってるかもしれない
酸っぱくなってたり古い値が残ったままになってるのに気づかずに使ってしまうのを防ぐ方法
デバッグやアサーションを使って、妥当な値が含まれていることを確認する
変数を使い終えたら意味のない値を設定しておく
ポインタを削除したらnullを代入しておくなど
データが永続的ではないことを前提としたコードを書く
ルーチンを終了した時の変数の値が次にルーチンを実行した時もそのまま残ってると決め込んではダメ
C++, Javaのstaticのように値が保たれることを保証する言語固有の機能を使う場合は例外
データは使用する直前に宣言して初期化する
10.6 バインディングタイム
バインディングタイムとは、変数にその値が結び付けられるタイミングのことを言う(Thimbleby1988)。
プログラムの保守性や更新性に広い範囲で関わってくる
コードを書いたときにバインドされるのか?コンパイルしたとき、ロードしたとき、プログラムを実行したとき、別のタイミングだろうか?
一般的にはバインディングのタイミングが遅ければ遅いほど、コードの柔軟性が増していく
色々なバインディングタイム
コードを書いたときに変数をバインドする
code: java
titleBar.color = 0xFF;
ハードコーディングされたリテラル値
これはよくない
ここを変更したら、これと同じ値でないといけない他の部分と生合成が取れなくなる
コンパイル時に変数をバインドする
code: java
private static final int COLOR_BLUE = 0xFF;
private static final int TITLE_BAR_COLOR = COLOR_BLUE;
titleBar.color = TITLE_BAR_COLOR;
TITLE_BAR_COLORはコンパイル時に実際の値に置き換えられる
ハードコーディングより良い
定数の値を変更すれば他の全ての場所に変更が反映されるので
実行時に変数をバインドする
code: java
titleBar.color = ReadTitleColor();
このメソッドはプログラムの実行中にどこからかの設定ファイルから値を読み取るルーチン
色を変えたいときはメソッドが読み取る設定ファイルの内容を変更すれば良い
ユーザーがカスタマイズすることもできる
バインディングタイムまとめ
■コーディング時(マジックナンバーを使用する)
■コンパイル時(名前付き定数を使用する)
■ロード時(WindowsのレジストリファイルやJavaのプロパティファイルといった外部ソースから値を読み取る)
■オブジェクトのインスタンス生成時(ウィンドウを作成するたびに値を読み取るなど)
■ジャストインタイム(ウィンドウを描画するたびに値を読み取るなど)
バインディングタイムが早ければ早いほど柔軟性がなくなり複雑でなくなる
遅くすれば柔軟性が得られるが、その柔軟性をサポートするのに必要なコードが複雑になる
プログラミングの成功の行方は、複雑さをどれだけ抑えられるかにかかっている。ソフトウェアの要件を満たすために必要な柔軟性を組み込み、それ以上の柔軟性(および、それに伴う複雑さ)にこだわらないのが、腕の立つプログラマである。
10.7 データ型と制御構造の関係
3種類のデータとそれらに対応する制御構造との関係
連続データはプログラムにおいて連続的なステートメントに変換される
5種類の値を処理するステートメントが5つ並んでいるとしたら、それらは連続的なステートメントである。
https://gyazo.com/e6f4746398b670d70e59e5f5837a246a
選択データはプログラムにおいてif文やcase文に変換される
https://gyazo.com/b071bb6cd94397bdc68f4f9785d95ad5
反復データはプログラムにおいてfor、repeat、whileといったループ構造に変換される
https://gyazo.com/a0642832faccc09283f3e425d40b5f0a
10.8 1つの目的に1つの変数
変数は複数の目的に使うことができるが余計なことはしないほうがいい
変数は1つの目的にのみ使用する
1つの変数を異なる作業に使いたいことがある
こういう場合はどちらかの用途にふさわしくない名前がついているか、両方にtempみたいな名前がついている
隠れた意味を持つ変数は使わない
以下のように二重の意味を持たせないこと
変数pageCountは印刷するページ数を表すが、-1の場合はエラーを表す
変数customerIdは500,000未満の場合は顧客番号を表すが、それ以上の場合はそこから500,000を引いたものが滞納している口座の数になる
変数bytesWrittenは出力ファイルに書き込まれるバイト数を表すが、値が負の場合は出力に使用するディスクドライブの数を表す
このような変数の乱用を「ハイブリッド結合」という
変数の型がどちらか一方の作業とは合致しないことを意味する
宣言された変数がすべて使用されることを確認する
。Card、Church、Agrestiの研究によれば、参照されない変数は欠陥発生率の高さと因果関係にある(Card,ChurchandAgresti1986)
10.9 まとめ
本文を参照